package top.zhangjianyong.tools.indicator.strategy.impl;

import org.springframework.stereotype.Component;
import top.zhangjianyong.tools.entity.ETFData;
import top.zhangjianyong.tools.indicator.context.IndicatorContext;
import top.zhangjianyong.tools.indicator.model.TechnicalIndicatorResult;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;

/**
 * CCI（商品通道指数）指标计算器
 * 窗口20，数据量>100时计算
 *
 * @author zhangjianyong
 * @date 2024/01/01
 */
@Component
public class CCIIndicator extends AbstractIndicator {

    private static final String INDICATOR_NAME = "CCI";
    private static final int PERIOD = 20;
    private static final int MIN_DATA_SIZE_FOR_CALCULATION = 100;

    @Override
    public String getName() {
        return INDICATOR_NAME;
    }

    @Override
    public int getMinDataSize() {
        return PERIOD;
    }

    @Override
    public boolean canCalculate(IndicatorContext context) {
        // 根据任务要求，数据量>100时计算
        return super.canCalculate(context) && context.getDataSize() > MIN_DATA_SIZE_FOR_CALCULATION;
    }

    @Override
    protected TechnicalIndicatorResult doCalculate(IndicatorContext context) {
        List<ETFData> dataList = context.getDataList();

        // 取最后PERIOD个数据点
        List<ETFData> subList = dataList.subList(dataList.size() - PERIOD, dataList.size());

        // 计算典型价格（由于只有收盘价，使用收盘价）
        BigDecimal typicalPrice = dataList.get(dataList.size() - 1).getValue();

        // 计算典型价格的PERIOD日简单移动平均
        BigDecimal sma = calculateSMA(subList);

        // 计算平均绝对偏差
        BigDecimal meanDeviation = calculateMeanDeviation(subList, sma);

        // 计算CCI
        BigDecimal cci = BigDecimal.ZERO;
        if (meanDeviation.compareTo(BigDecimal.ZERO) != 0) {
            BigDecimal constant = new BigDecimal("0.015"); // CCI常数
            cci = typicalPrice.subtract(sma)
                .divide(constant.multiply(meanDeviation), 4, RoundingMode.HALF_UP);
        }

        TechnicalIndicatorResult result = TechnicalIndicatorResult.success(getName());
        result.addValue("cci", cci.setScale(2, RoundingMode.HALF_UP));
        result.addValue("cci_20", cci.setScale(2, RoundingMode.HALF_UP));

        return result;
    }

    /**
     * 计算SMA
     */
    private BigDecimal calculateSMA(List<ETFData> dataList) {
        BigDecimal sum = BigDecimal.ZERO;
        for (ETFData data : dataList) {
            sum = sum.add(data.getValue());
        }
        return sum.divide(new BigDecimal(dataList.size()), 4, RoundingMode.HALF_UP);
    }

    /**
     * 计算平均绝对偏差
     */
    private BigDecimal calculateMeanDeviation(List<ETFData> dataList, BigDecimal sma) {
        BigDecimal sumDeviation = BigDecimal.ZERO;
        for (ETFData data : dataList) {
            BigDecimal deviation = data.getValue().subtract(sma).abs();
            sumDeviation = sumDeviation.add(deviation);
        }
        return sumDeviation.divide(new BigDecimal(dataList.size()), 4, RoundingMode.HALF_UP);
    }
}

